import { LinkStyle } from "../Constants";
import { Block, GridSystem, MyArrow, MyPolygon, MyLine } from "../Index";
import IPoint from "../interface/IPoint";
import { getUuid, calculateBezierPointForCubic, getCenterPoint } from "../utils";
import MyControlPoint from "./MyControlPoint";
import Node from "./Node";

class Line extends Node {

    id: string  // 唯一id
    name: string  //名称
    class: string;  // 类名

    // 基本属性
    points: IPoint[]  // 坐标点集合
    actualPoints: IPoint[];  // 实际得到的点数
    lineWidth: number;  // 线的宽度
    strokeStyle: string;  // 线的颜色
    pattern: CanvasPattern | null  // 填充
    segments: number[] = [15, 15]   // 虚线间隔
    lineDashOffset: number;  // 虚线移动
    connectStyle: LinkStyle; // 线段连接的方式,直线,折现,曲线
    smoothness: number  // 光滑程度,差值越多越光滑
    zIndex: number;  // 层级关系

    // 控制点相关
    controlPoints: MyControlPoint[];  //控制点集合
    parentBlock: Line | null; // 关联父元素
    transformPoint: Block | null;   // 被拖动的控制点
    children: Block[];  // 子节点

    // 节点状态
    isFixedSize: boolean // 是否固定大小，不随网格缩放
    isFixedPos: boolean;  // 是否绝对位置.不跟随网格移动
    isPointIn: boolean //鼠标是否悬浮在元素上
    isGrouped: boolean // 是否拖拽中
    isFocus: boolean  // 是否获取到焦点
    isDraged: boolean;  // 是否在拖拽中

    arrows: MyArrow[];  // 箭头
    lastDistArr: IPoint[] | null;  // 记录之前移动过得距离
    cbSelect: boolean;  // 是否可被选择
    cbMove: boolean; // 是否可被移动
    cbTransform: boolean; // 是否调整, 缩放旋转等
    cbAdsorption: boolean;  // 是否开启吸附效果
    mouseOn: Function | null;  // 如果有，鼠标悬浮后就会被调用
    mouseDown: Function | null;  // 如果有，鼠标点击后就会被调用
    mouseLeave: Function | null;  // 如果有，鼠标离开后就会被调用

    constructor(points: IPoint[] = []) {
        super();
        this.id = getUuid();
        this.name = '';
        this.class = ''

        this.points = points;
        this.actualPoints = []
        this.lineWidth = 2;
        this.strokeStyle = '#ffa940';
        this.pattern = null;
        this.lineDashOffset = 2;
        this.connectStyle = LinkStyle.STRAIGHT;
        this.smoothness = 30;
        this.zIndex = 0;
        this.lastDistArr = [];

        // 节点关联元素
        this.arrows = []
        this.controlPoints = [];
        this.parentBlock = null;
        this.transformPoint = null;
        this.children = []

        // 节点状态
        this.isDraged = false;
        this.isFixedSize = false;
        this.isFixedPos = false;
        this.isPointIn = false;
        this.isGrouped = false;
        this.isFocus = false;

        // 节点功能
        this.cbTransform = GridSystem.cbTransform;
        this.cbAdsorption = GridSystem.cbAdsorption;
        this.cbTransform = GridSystem.cbTransform;
        this.cbSelect = GridSystem.cbSelect;
        this.cbMove = GridSystem.cbMove;

        // 节点事件
        this.mouseOn = null;
        this.mouseDown = null;
        this.mouseLeave = null;
    }

    draw(points: IPoint[] = []): void {
        this.actualPoints = this.getNewPointsByStyle(points);
        let ctx = GridSystem.gls.ctx;
        let firstPoint = this.actualPoints[0];
        let { x: x1, y: y1 } = firstPoint;
        ctx.save();
        ctx.beginPath()
        ctx.moveTo(x1, y1)
        this.actualPoints.forEach(point => {
            let { x, y } = point;
            ctx.lineTo(x, y);
        })
        ctx.lineWidth = this.lineWidth;
        ctx.lineCap = "round"
        ctx.strokeStyle = this.strokeStyle
        if (this.segments && this.segments.length > 0) {
            ctx.setLineDash(this.segments);
        }
        if (this.pattern) {
            ctx.fillStyle = this.pattern;
        }
        ctx.lineDashOffset = this.lineDashOffset;
        ctx.lineWidth = 2;
        ctx.stroke();
        ctx.closePath();
        ctx.restore();
        this.hasPointIn(GridSystem.gls.mouseMovePoint)
        this.setControlPoints(points);  // 设置控制点
        this.update(points);  // 更新原始点
        this.updateActuals(this.actualPoints);  // 更新实际扩展的点
    }

    hasPointIn(point?: IPoint | null) {
        if (point) {
            let ctx = GridSystem.gls.ctx;
            this.isPointIn = ctx.isPointInStroke(point.x, point.y) || ctx.isPointInStroke(point.x - 2, point.y - 2) || ctx.isPointInStroke(point.x + 2, point.y + 2)
                || ctx.isPointInStroke(point.x + 2, point.y) || ctx.isPointInStroke(point.x - 2, point.y) || ctx.isPointInStroke(point.x, point.y + 2) || ctx.isPointInStroke(point.x, point.y - 2);
        }
    }

    /**
     * 渲染子元素
     * @param px  父元素的绝对坐标
     * @param py  
     * @param children 父元素的子元素们
     */
    drawChildBlock(startPoint: IPoint, endPoint: IPoint, children: Block[]) {
        let that = this;
        children.forEach(block => {
            if (block.parentBlock) {
                let centerPoint = GridSystem.gls.getPixelPos(getCenterPoint(startPoint, endPoint));
                let width = GridSystem.gls.getPixelSize(block.width)
                let height = GridSystem.gls.getPixelSize(block.height);
                let x = GridSystem.gls.getPixelSize(block.x);
                let y = GridSystem.gls.getPixelSize(block.y);
                // 这里我默认是居中的,block的x,y是在居中的基础上去位移的
                block.draw(centerPoint.x + x, centerPoint.y + y, width, height);
                // 线状的子元素就没必要跟点状元素一样递归了,如果有需要可以把嵌套后的点状元素当做他的子元素;
            }
        })
    }

    update(points: IPoint[] = []) {
        points.forEach((point, index) => {
            if (this.points[index]) {
                this.points[index].x = GridSystem.gls.getRelativePosX(point.x);
                this.points[index].y = GridSystem.gls.getRelativePosY(point.y);
            }
        })
        if (points.length >= 2) {
            this.drawChildBlock(points[0], points[points.length - 1], this.children)  // 绘制嵌套的子元素坐标
        }
    }

    updateActuals(points: IPoint[] = []) {
        points.forEach((point, index) => {
            if (this.points[index] && !point.isNew) {
                this.points[index].x = GridSystem.gls.getRelativePosX(point.x);
                this.points[index].y = GridSystem.gls.getRelativePosY(point.y);
            }
        })
    }

    // 根据原始点插值称为新的线段
    getNewPointsByStyle(points: IPoint[]): IPoint[] {
        let newPoints: IPoint[] = [...points]
        switch (this.connectStyle) {
            case LinkStyle.BROKEN:  // 折线
                newPoints.forEach((point, index) => {
                    if (index != 0) {
                        const prePoint = points[index - 1];
                        const curPoint = points[index];
                        let newPoint1 = { x: prePoint.x, y: (curPoint.y + prePoint.y) / 2, isNew: true };
                        let newPoint2 = { x: curPoint.x, y: (curPoint.y + prePoint.y) / 2, isNew: true };
                        newPoints.splice(index, 0, newPoint1, newPoint2);
                    }
                })
                break;
            case LinkStyle.CURVE:   // 曲线      
                let curveDegreeY = GridSystem.gls.getPixelSize(Math.abs(newPoints[1].y - newPoints[0].y) / (GridSystem.gls.scale * 2));
                let curveDegreeX = GridSystem.gls.getPixelSize(Math.abs(newPoints[1].x - newPoints[0].x) / (GridSystem.gls.scale * 2));
                let centerPoint = { x: newPoints[0].x + (newPoints[1].x - newPoints[0].x) / 2, y: newPoints[0].y + (newPoints[1].y - newPoints[0].y) / 2 }   // 两点之间的中点
                let controlP1 = { x: newPoints[0].x + (centerPoint.x - newPoints[0].x) / 8, y: newPoints[0].y + (centerPoint.y - newPoints[0].y) / 8 }   // 控制点1
                if (newPoints[1].y > newPoints[0].y) {
                    controlP1.y += curveDegreeY;
                } else {
                    controlP1.y -= curveDegreeY;
                }
                let controlP2 = { x: centerPoint.x + (newPoints[1].x - centerPoint.x) / 5, y: centerPoint.y + (newPoints[1].y - centerPoint.y) / 5 }   // 控制点2
                if (newPoints[1].x > newPoints[0].x) {
                    controlP2.x += curveDegreeX;
                } else {
                    controlP2.x -= curveDegreeX;
                }
                let curvePoints: IPoint[] = []
                for (let i = 0; i <= this.smoothness; i++) { // 线性插值
                    let point = calculateBezierPointForCubic(i / this.smoothness, newPoints[0], controlP1, controlP2, newPoints[1])  // 三次贝塞尔曲线
                    if (i != 0 && i != this.smoothness - 1) {
                        point.isNew = true;  // 只有起点和终点是原始点,中间都是计算得来的
                    }
                    if (i != this.smoothness) {
                        curvePoints.push(point)
                    }
                }
                newPoints = curvePoints;
                break;
            case LinkStyle.STRAIGHT:   // 直线   
                newPoints.forEach((point, index) => {
                    if (index != 0) {  // 硬塞两个
                        const startPoint = {
                            x: points[0].x,
                            y: points[0].y,
                            isNew: true
                        };
                        const endPoint = {
                            x: points[1].x,
                            y: points[1].y,
                            isNew: true
                        };
                        newPoints.splice(index, 0, startPoint, endPoint);
                    }
                })
                break;
            default:
                break;
        }
        return newPoints;
    }

    setControlPoints(points: IPoint[] = []) {   // 实际点,实际像素
        if (this.cbTransform) {
            GridSystem.gls.setLineControls(this, points);
        }
    }

    setParentBlock(block: Line) {
        this.parentBlock = block;
    }

    drawControls(points: IPoint[] = []) {  // 实际像素
        points.forEach((point, index) => {
            let controlPoint = this.controlPoints[index];
            //     controlPoint.angle = this.angle;
            controlPoint.drawPonit(controlPoint.getLeftTopPointPosition(point.x, point.y))
        })
    }

    addPoint(point: IPoint) {
        this.points.push(point)
    }


    toFixedPos() {
        // if (!this.isFixedPos) {
        //     let { x, y } = GridSystem.GridSystem.gls.getPixelPos({ x: this.x, y: this.y });
        //     this.x = x;
        //     this.y = y;
        // }
        this.isFixedPos = true;
    }

    toRelativePos() {
        // if (this.isFixedPos) {
        //     let { x, y } = GridSystem.GridSystem.gls.getRelativePos({ x: this.x, y: this.y });
        //     this.x = x;
        //     this.y = y;
        // }
        this.isFixedPos = false;
    }

    toFixedSize() { }
    toRelativeSize() { }

    addBlock(block: Block) {
        block.parentBlock = this;
        this.children.push(block)
    }

    removeBlock() {

    }

    popBlock() {
        this.children.pop()
    }
}

export default Line;